package service

import (
	"fmt"
	"strings"

	"github.com/hyperledger/fabric-samples/asset-kx/chaincode-go/chaincode/asset/api"
	assetruleapi "github.com/hyperledger/fabric-samples/asset-kx/chaincode-go/chaincode/assetrule/api"
	ledgerapi "github.com/hyperledger/fabric-samples/asset-kx/chaincode-go/chaincode/ledger-api"
	"github.com/prometheus/common/log"
)

const (
	AssetLoanKeyType = "assetNo~loanNo~uTxid"
	LoanAssetKeyType = "loanNo~assetNo~uTxid"
)

type SmartAssetContract struct {
	AssetRuleSupportApi assetruleapi.AssetRuleSupportApi `inject:"AssetRuleSupportApi"`
}

func (s *SmartAssetContract) CreateAsset(ctx ledgerapi.TransactionContextInterface, assetContent string) error {
	asset, err := assetapi.Unmarshal([]byte(assetContent))
	if err != nil {
		return err
	}
	exists, err := s.AssetExists(ctx, asset.GetAssetNo())
	if err != nil {
		return err
	} else if exists {
		return fmt.Errorf("assetNo[%s] exists", asset.GetAssetNo())
	}

	if asset.GetValuationAmount() == "" {
		valuationAmount, err := s.AssetRuleSupportApi.ValuateAsset(ctx, asset, "")
		if err != nil {
			log.Info("can not valuate asset, error info: ", err)
		} else {
			asset.SetValuationAmount(valuationAmount)
		}
	}

	stateDb := ctx.GetStateDb()
	err = stateDb.SetEvent("CreateAsset", asset.GetAssetNo())
	if err != nil {
		return err
	}
	return stateDb.Add(asset)
}

func (s *SmartAssetContract) ReadAssets(ctx ledgerapi.TransactionContextInterface, assetNos ...string) ([]assetapi.Asset, error) {
	if len(assetNos) == 0 {
		return nil, nil
	}
	var assets []assetapi.Asset
	for _, assetNo := range assetNos {
		asset, err := s.ReadAsset(ctx, assetNo)
		if err != nil {
			continue
		}
		assets = append(assets, asset)
	}
	return assets, nil
}

//当资产不存在时，返回错误
func (s *SmartAssetContract) ReadAsset(ctx ledgerapi.TransactionContextInterface, assetNo string) (assetapi.Asset, error) {
	assetNo = strings.TrimSpace(assetNo)
	stateDb := ctx.GetStateDb()
	
	if assetState, err := stateDb.Get(assetapi.DataType, assetNo, assetapi.UnmarshalStateData); err != nil {
		return nil, err
	} else {
		if assetState == nil {
			return nil, fmt.Errorf("can not find asset by assetNo:%s", assetNo)
		}
		
		return assetState.(assetapi.Asset), nil
	}
}
func (s *SmartAssetContract) AssetExists(ctx ledgerapi.TransactionContextInterface, assetNo string) (bool, error) {
	if assetState, err := ctx.GetStateDb().Get(assetapi.DataType, assetNo, assetapi.UnmarshalStateData); err != nil {
		return false, err
	} else {
		return assetState != nil, err
	}
}

func (s *SmartAssetContract) DelAssets(ctx ledgerapi.TransactionContextInterface, assetNos ...string) error {
	stub := ctx.GetStub()
	stateDb := ctx.GetStateDb()
	for _, assetNo := range assetNos {
		stateDb.Del(assetapi.DataType, strings.TrimSpace(assetNo))
		result, err := stub.GetStateByPartialCompositeKey(AssetLoanKeyType, []string{assetNo})
		if err != nil {
			log.Error("del asset-loan error.", err)
			continue
		}
		defer result.Close()
		for result.HasNext() {
			next, err := result.Next()
			if err != nil {
				log.Error("del asset-loan error.", err)
				continue
			}
			stub.DelState(next.Key)
		}
	}
	return nil
}

func (s *SmartAssetContract) ValuateAsset(ctx ledgerapi.TransactionContextInterface, assetNo string, assetRuleContent string) (string, error) {
	assetFromDb, err := s.ReadAsset(ctx, assetNo)
	if err != nil {
		return "", err
	}
	assetValAmount, err := s.AssetRuleSupportApi.ValuateAsset(ctx, assetFromDb, assetRuleContent)
	if err != nil {
		return "", err
	}
	assetFromDb.SetValuationAmount(assetValAmount)

	stateDb := ctx.GetStateDb()

	err = stateDb.Update(assetFromDb)
	if err != nil {
		return "", err
	}
	err = stateDb.SetEvent("ValuateAsset", assetNo)
	if err != nil {
		return "", err
	}
	return assetValAmount, err
}
